﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace cmdLineParserModule.HelperClasses
{
    /// <summary>
    /// Provides interface for type-related work
    /// </summary>
    internal static class TypeManager
    {
        /// <summary>
        /// When Nullable<T> pased, T returned, else returns type"/>
        /// </summary>
        /// <param name="type">Ordinary or nullable type</param>
        /// <returns></returns>
        internal static Type getPureType(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(System.Nullable<>)))
            {
                var genericTypes = type.GetGenericArguments();
                if (genericTypes.Count() == 1)
                    return genericTypes.Single();
                else
                    throw new WrongDefinitionException(string.Format("Cannot get pure type from type {0}", type.Name));
            }
            else
                return type;
        }

        /// <summary>
        /// Gets option class from marked property
        /// </summary>
        /// <param name="field">Field marked by option class</param>
        /// <returns>Option class that field is marked with</returns>
        internal static optionAttribute getOptionAttribute(FieldInfo field)
        {
            IEnumerable<optionAttribute> OptionedFields = from pro in field.GetCustomAttributes(false)
                                                          where pro is optionAttribute
                                                          select (optionAttribute)pro;

            switch (OptionedFields.Count())
            {
                case 0: return null;
                case 1: return OptionedFields.Single();
                default: throw new WrongDefinitionException(string.Format("Too many options at field {0}, only one per field allowed.", field.Name));
            }
        }

        /// <summary>
        /// Gets all fields marked by option class and the option class
        /// </summary>
        /// <param name="fields">Field list</param>
        /// <returns>Marked fields</returns>
        internal static IEnumerable<KeyValuePair<FieldInfo, optionAttribute>> getOptionedFields(FieldInfo[] fields)
        {
            // creates pair (field, optionattribute) list

            Dictionary<FieldInfo, optionAttribute> pairList =
                (
                from field in fields
                select new KeyValuePair<FieldInfo, optionAttribute>(field, getOptionAttribute(field))
                )
                .ToDictionary(pair => pair.Key, pair => pair.Value);

            // here we filter only fields marked by option* class
            var optionedFields = from pair in pairList
                                 where pair.Value != null
                                 select pair;

            return optionedFields;
        }
    }
}
